/*************************************************************************/ /**
                                                                             *****************************************************************************
                                                                             * @file   include/sys/hd.h
                                                                             * @brief
                                                                             * @author Forrest Y. Yu
                                                                             * @date   2008
                                                                             *****************************************************************************
                                                                             *****************************************************************************/

#ifndef _ORANGES_HD_H_
#define _ORANGES_HD_H_

/**
 * @struct part_ent
 * @brief  Partition Entry struct.
 *
 * <b>Master Boot Record (MBR):</b>
 *   Located at offset 0x1BE in the 1st sector of a disk. MBR contains
 *   four 16-byte partition entries. Should end with 55h & AAh.
 *
 * <b>partitions in MBR:</b>
 *   A PC hard disk can contain either as many as four primary partitions,
 *   or 1-3 primaries and a single extended partition. Each of these
 *   partitions are described by a 16-byte entry in the Partition Table
 *   which is located in the Master Boot Record.
 *
 * <b>extented partition:</b>
 *   It is essentially a link list with many tricks. See
 *   http://en.wikipedia.org/wiki/Extended_boot_record for details.
 */
struct part_ent
{
    u8 boot_ind; /**
                  * boot indicator
                  *   Bit 7 is the active partition flag,
                  *   bits 6-0 are zero (when not zero this
                  *   byte is also the drive number of the
                  *   drive to boot so the active partition
                  *   is always found on drive 80H, the first
                  *   hard disk).
                  */

    u8 start_head; /**
                    * Starting Head
                    */

    u8 start_sector; /**
                      * Starting Sector.
                      *   Only bits 0-5 are used. Bits 6-7 are
                      *   the upper two bits for the Starting
                      *   Cylinder field.
                      */

    u8 start_cyl; /**
                   * Starting Cylinder.
                   *   This field contains the lower 8 bits
                   *   of the cylinder value. Starting cylinder
                   *   is thus a 10-bit number, with a maximum
                   *   value of 1023.
                   */

    u8 sys_id; /**
                * System ID
                * e.g.
                *   01: FAT12
                *   81: MINIX
                *   83: Linux
                */

    u8 end_head; /**
                  * Ending Head
                  */

    u8 end_sector; /**
                    * Ending Sector.
                    *   Only bits 0-5 are used. Bits 6-7 are
                    *   the upper two bits for the Ending
                    *    Cylinder field.
                    */

    u8 end_cyl; /**
                 * Ending Cylinder.
                 *   This field contains the lower 8 bits
                 *   of the cylinder value. Ending cylinder
                 *   is thus a 10-bit number, with a maximum
                 *   value of 1023.
                 */

    u32 start_sect; /**
                     * starting sector counting from
                     * 0 / Relative Sector. / start in LBA
                     */

    u32 nr_sects; /**
                   * nr of sectors in partition
                   */

} PARTITION_ENTRY;

/********************************************/
/* I/O Ports used by hard disk controllers. */
/********************************************/
/* slave disk not supported yet, all master registers below */

/* Command Block Registers */
/*	MACRO		PORT			DESCRIPTION			INPUT/OUTPUT	*/
/*	-----		----			-----------			------------	*/
#define REG_DATA 0x1F0         /*	Data				I/O		*/
#define REG_FEATURES 0x1F1     /*	Features			O		*/
#define REG_ERROR REG_FEATURES /*	Error				I		*/
                               /* 	The contents of this register are valid only when the error bit
                                   (ERR) in the Status Register is set, except at drive power-up or at the
                                   completion of the drive's internal diagnostics, when the register
                                   contains a status code.
                                   When the error bit (ERR) is set, Error Register bits are interpreted as such:
                                   |  7  |  6  |  5  |  4  |  3  |  2  |  1  |  0  |
                                   +-----+-----+-----+-----+-----+-----+-----+-----+
                                   | BRK | UNC |     | IDNF|     | ABRT|TKONF| AMNF|
                                   +-----+-----+-----+-----+-----+-----+-----+-----+
                                      |     |     |     |     |     |     |     |
                                      |     |     |     |     |     |     |     `--- 0. Data address mark not found after correct ID field found
                                      |     |     |     |     |     |     `--------- 1. Track 0 not found during execution of Recalibrate command
                                      |     |     |     |     |     `--------------- 2. Command aborted due to drive status error or invalid command
                                      |     |     |     |     `--------------------- 3. Not used
                                      |     |     |     `--------------------------- 4. Requested sector's ID field not found
                                      |     |     `--------------------------------- 5. Not used
                                      |     `--------------------------------------- 6. Uncorrectable data error encountered
                                      `--------------------------------------------- 7. Bad block mark detected in the requested sector's ID field
                               */
#define REG_NSECTOR 0x1F2      /*	Sector Count			I/O		*/
#define REG_LBA_LOW 0x1F3      /*	Sector Number / LBA Bits 0-7	I/O		*/
#define REG_LBA_MID 0x1F4      /*	Cylinder Low / LBA Bits 8-15	I/O		*/
#define REG_LBA_HIGH 0x1F5     /*	Cylinder High / LBA Bits 16-23	I/O		*/
#define REG_DEVICE 0x1F6       /*	Drive | Head | LBA bits 24-27	I/O		*/
                               /*	|  7  |  6  |  5  |  4  |  3  |  2  |  1  |  0  |
                                   +-----+-----+-----+-----+-----+-----+-----+-----+
                                   |  1  |  L  |  1  | DRV | HS3 | HS2 | HS1 | HS0 |
                                   +-----+-----+-----+-----+-----+-----+-----+-----+
                                            |           |   \_____________________/
                                            |           |              |
                                            |           |              `------------ If L=0, Head Select.
                                            |           |                                    These four bits select the head number.
                                            |           |                                    HS0 is the least significant.
                                            |           |                            If L=1, HS0 through HS3 contain bit 24-27 of the LBA.
                                            |           `--------------------------- Drive. When DRV=0, drive 0 (master) is selected.
                                            |                                               When DRV=1, drive 1 (slave) is selected.
                                            `--------------------------------------- LBA mode. This bit selects the mode of operation.
                                                                                               When L=0, addressing is by 'CHS' mode.
                                                                                               When L=1, addressing is by 'LBA' mode.
                               */
#define REG_STATUS 0x1F7       /*	Status				I		*/
                               /* 	Any pending interrupt is cleared whenever this register is read.
                                   |  7  |  6  |  5  |  4  |  3  |  2  |  1  |  0  |
                                   +-----+-----+-----+-----+-----+-----+-----+-----+
                                   | BSY | DRDY|DF/SE|  #  | DRQ |     |     | ERR |
                                   +-----+-----+-----+-----+-----+-----+-----+-----+
                                      |     |     |     |     |     |     |     |
                                      |     |     |     |     |     |     |     `--- 0. Error.(an error occurred)
                                      |     |     |     |     |     |     `--------- 1. Obsolete.
                                      |     |     |     |     |     `--------------- 2. Obsolete.
                                      |     |     |     |     `--------------------- 3. Data Request. (ready to transfer data)
                                      |     |     |     `--------------------------- 4. Command dependent. (formerly DSC bit)
                                      |     |     `--------------------------------- 5. Device Fault / Stream Error.
                                      |     `--------------------------------------- 6. Drive Ready.
                                      `--------------------------------------------- 7. Busy. If BSY=1, no other bits in the register are valid.
                               */
#define STATUS_BSY 0x80
#define STATUS_DRDY 0x40
#define STATUS_DFSE 0x20
#define STATUS_DSC 0x10
#define STATUS_DRQ 0x08
#define STATUS_CORR 0x04
#define STATUS_IDX 0x02
#define STATUS_ERR 0x01

#define REG_CMD REG_STATUS /*	Command				O		*/
                           /*
                               +--------+---------------------------------+-----------------+
                               | Command| Command Description             | Parameters Used |
                               | Code   |                                 | PC SC SN CY DH  |
                               +--------+---------------------------------+-----------------+
                               | ECh  @ | Identify Drive                  |             D   |
                               | 91h    | Initialize Drive Parameters     |    V        V   |
                               | 20h    | Read Sectors With Retry         |    V  V  V  V   |
                               | E8h  @ | Write Buffer                    |             D   |
                               +--------+---------------------------------+-----------------+
                           
                               KEY FOR SYMBOLS IN THE TABLE:
                               ===========================================-----=========================================================================
                               PC    Register 1F1: Write Precompensation	@     These commands are optional and may not be supported by some drives.
                               SC    Register 1F2: Sector Count		D     Only DRIVE parameter is valid, HEAD parameter is ignored.
                               SN    Register 1F3: Sector Number		D+    Both drives execute this command regardless of the DRIVE parameter.
                               CY    Register 1F4+1F5: Cylinder low + high	V     Indicates that the register contains a valid paramterer.
                               DH    Register 1F6: Drive / Head
                           */

/* Control Block Registers */
/*	MACRO		PORT			DESCRIPTION			INPUT/OUTPUT	*/
/*	-----		----			-----------			------------	*/
#define REG_DEV_CTRL 0x3F6          /*	Device Control			O		*/
                                    /*	|  7  |  6  |  5  |  4  |  3  |  2  |  1  |  0  |
                                        +-----+-----+-----+-----+-----+-----+-----+-----+
                                        | HOB |  -  |  -  |  -  |  -  |SRST |-IEN |  0  |
                                        +-----+-----+-----+-----+-----+-----+-----+-----+
                                           |                             |     |
                                           |                             |     `--------- Interrupt Enable.
                                           |                             |                  - IEN=0, and the drive is selected,
                                           |                             |                    drive interrupts to the host will be enabled.
                                           |                             |                  - IEN=1, or the drive is not selected,
                                           |                             |                    drive interrupts to the host will be disabled.
                                           |                             `--------------- Software Reset.
                                           |                                                - The drive is held reset when RST=1.
                                           |                                                  Setting RST=0 re-enables the drive.
                                           |                                                - The host must set RST=1 and wait for at least
                                           |                                                  5 microsecondsbefore setting RST=0, to ensure
                                           |                                                  that the drive recognizes the reset.
                                           `--------------------------------------------- HOB (High Order Byte)
                                                                                            - defined by 48-bit Address feature set.
                                    */
#define REG_ALT_STATUS REG_DEV_CTRL /*	Alternate Status		I		*/
                                    /*	This register contains the same information as the Status Register.
                                        The only difference is that reading this register does not imply interrupt acknowledge or clear a pending interrupt.
                                    */

#define REG_DRV_ADDR 0x3F7 /*	Drive Address			I		*/

#define MAX_IO_BYTES 256 /* how many sectors does one IO can handle */

struct hd_cmd
{
    u8 features;
    u8 count;
    u8 lba_low;
    u8 lba_mid;
    u8 lba_high;
    u8 device;
    u8 command;
};

struct part_info
{
    u32 base; /* # of start sector (NOT byte offset, but SECTOR) */
    u32 size; /* how many sectors in this partition */
};

/* main drive struct, one entry per drive */
struct hd_info
{
    int open_cnt;
    struct part_info primary[NR_PRIM_PER_DRIVE];
    struct part_info logical[NR_SUB_PER_DRIVE];
};

/***************/
/* DEFINITIONS */
/***************/
#define HD_TIMEOUT 10000 /* in millisec */
#define PARTITION_TABLE_OFFSET 0x1BE
#define ATA_IDENTIFY 0xEC
#define ATA_READ 0x20
#define ATA_WRITE 0x30
/* for DEVICE register. */
#define MAKE_DEVICE_REG(lba, drv, lba_highest) (((lba) << 6) | \
                                                ((drv) << 4) | \
                                                (lba_highest & 0xF) | 0xA0)

#endif /* _ORANGES_HD_H_ */
